package com.jiyun.jiyunmall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jiyun.jiyunmall.common.dto.SkuEsModel;
import com.jiyun.jiyunmall.common.dto.ware.SkuHasStockDTO;
import com.jiyun.jiyunmall.common.service.impl.CrudServiceImpl;
import com.jiyun.jiyunmall.common.utils.Result;
import com.jiyun.jiyunmall.product.dao.*;
import com.jiyun.jiyunmall.product.dto.*;
import com.jiyun.jiyunmall.product.entity.*;
import com.jiyun.jiyunmall.product.feign.CouponFeignService;
import com.jiyun.jiyunmall.product.feign.SearchFeignService;
import com.jiyun.jiyunmall.product.feign.WareFeignService;
import com.jiyun.jiyunmall.product.service.*;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jcajce.provider.digest.MD2;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * spu信息
 *
 * @author boge 71164376@qq.com
 * @since 1.0.0 2022-07-12
 */
@Service
public class SpuInfoServiceImpl extends CrudServiceImpl<SpuInfoDao, SpuInfoEntity, SpuInfoDTO> implements SpuInfoService {

    @Override
    public QueryWrapper<SpuInfoEntity> getWrapper(Map<String, Object> params) {
        String id = (String) params.get("id");

        QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(id), "id", id);

        return wrapper;
    }

    @Autowired
    SpuInfoDao spuInfoDao;
    @Autowired
    SpuImagesDao spuImagesDao;
    @Autowired
    SpuInfoDescDao spuInfoDescDao;
    @Autowired
    CouponFeignService couponFeignService;
    @Autowired
    ProductAttrValueDao productAttrValueDao;
    @Autowired
    SkuInfoDao skuInfoDao;
    @Autowired
    SkuImagesDao skuImagesDao;
    @Autowired
    SkuInfoService skuInfoService;
    @Autowired
    SkuSaleAttrValueDao skuSaleAttrValueDao;
    @Autowired
    SkuImagesService skuImagesService;
@Autowired
    BrandService brandService;
@Autowired
CategoryService categoryService;
    @Autowired
    ProductAttrValueService attrValueService;
@Autowired
WareFeignService wareFeignService;
    @Autowired
    SearchFeignService searchFeignService;
    @Override
    public void up(Long spuId) {
//1、查询sku信息 skuInfo,根据spuId去查询它对应的多个sku信息，返回一个List集合<Sku>
        //传入Map参数，key：列名，值：查询的字段
        QueryWrapper<SkuInfoEntity> qw = new QueryWrapper<>();
        qw.eq("spu_id",spuId);
        List<SkuInfoEntity> skus = skuInfoDao.selectList(qw);


//2、远程调用ware微服务，根据skuId查询它的库存，是否有库存？ware数据库.ware_ware_sku
    //收集所有的skuId
        List<Long> skuIds = skus.stream().map(skuInfo -> {
            return skuInfo.getSkuId();
        }).collect(Collectors.toList());

        // 发起针对ware远程微服务调用，查询库存，由多个查询库存请求变成一个查询库存请求
        Result<List<SkuHasStockDTO>> skusHasStock = wareFeignService.getSkusHasStock(skuIds);
        /*
               [
                {
                  skuId:1,
                  hasStock:true
                },
                {
                  skuId:2,
                  hasStock:false
                },
               ]
               思考：现在是所有的skuId和sku库存信息，都在一个list集合中
               后面我们遍历每一个sku封装对应的EsModel数据的时候，在进行封装库存信息的时候，一定
               遍历data数据，判断每一个元素的skuId是否是和当前封装的skuId相等，然后将其库存取出来。
               假设：20个sku，意味着遍历20次这个list集合？ 极大的浪费了cpu资源。

               解决方案：
                 在这个位置，咱们将查询到的库存信息，进行一次循环，将skuList转为Map集合，key：skuId，值：SkuHasStockDTO库存信息
                 后面再封装skkuESModel的时候，只需要通过这个Map集合进行getID取值的方式就得到库存信息了。

         */
        List<SkuHasStockDTO> data = skusHasStock.getData();
        /*
        Collectors.toMap(参数1,参数2)
        该方法表示将当前遍历的元素，转为一个Map集合，它的方法体中，就是封装了这个转Map的映射规则
        参数1：代表将来转换成Map以后，那个Key是什么
        参数2：代表将来转换成Map以后，那个值是什么
        参数1和参数2都是一个函数式的参数，在参数1和参数2里面，直接写转换代码就可以了。

         */
        Map<Long, Boolean> stockMap = data.stream().collect(Collectors.toMap(
                key -> {
            return key.getSkuId();
        }, value -> {
            return value.getHasStock();
        }));
//data.stream().collect(Collectors.toMap(new Function<SkuHasStockDTO, Object>() {
//    @Override
//    public Object apply(SkuHasStockDTO skuHasStockDTO) {
//        return skuHasStockDTO.getSkuId();
//        }}        ,
//         new Function<SkuHasStockDTO, Object>() {
//            @Override
//            public Object apply(SkuHasStockDTO skuHasStockDTO) {
//                return skuHasStockDTO.getHasStock();
//            }
//}));
//3、查询可以被检索的 基本属性
        //2.3 查询当前spu的所有可以用来被检索的基本属性值信息（attr_id、attr_name、attr_value）
        List<ProductAttrValueDTO> attrSearchList = attrValueService.searchBaseAttrBySpuId(spuId);
        // 把spu的可以被检索的基本属性们，封装为将来的ESModel中的Attr类型对象，这样，将来直接存到ESModel中
        List<SkuEsModel.Attr> attrs = attrSearchList.stream().map(item -> {
            SkuEsModel.Attr attr = new SkuEsModel.Attr();
            BeanUtils.copyProperties(item, attr);
            return attr;
        }).collect(Collectors.toList());
// 4、封装每一个sku的信息=>SkuEsModel
        List<SkuEsModel> skuEsModelList = skus.stream().map(sku -> {
            //数据填充，将sku信息统统封装到esModel对象中，sku基本信息、属性信息、库存信息
            //遍历的逻辑，主要目的就是为每一个sku生成其对应的esModel对象
            SkuEsModel skuEsModel = new SkuEsModel();
            // sku的各种基本信息放进去
            BeanUtils.copyProperties(sku, skuEsModel);
            //4.1 品牌表
            //4.2 分类表
            BrandDTO brandDTO = brandService.get(skuEsModel.getBrandId());
            skuEsModel.setBrandName(brandDTO.getName());
            skuEsModel.setBrandImg(brandDTO.getLogo());
            CategoryDTO categoryDTO = categoryService.get(skuEsModel.getCatalogId());
            skuEsModel.setCatalogName(categoryDTO.getName());
            //4.3 将上面查询出来的sku的库存设置进来
            // 有的sku信息，在表中没有库存数据，所以get没有库存信息的sku值，就是null
            Boolean aBoolean = stockMap.get(sku.getSkuId())==null?false:stockMap.get(sku.getSkuId());
            skuEsModel.setHasStock(aBoolean);
            //4.4 将上面的查询出来的检索属性，设置进来
            skuEsModel.setAttrs(attrs);
            //热度评分直接写一个假数据，热度评分的业务还是比较麻烦的。。。
            skuEsModel.setHotScore(100L);
            return skuEsModel;
        }).collect(Collectors.toList());
        //5、 调用es服务，然后将这些数据发送给es去保存
            searchFeignService.productStatsUp(skuEsModelList);

        //6、本地更新一下spu.publishStatus 由0 =>1就代表上架成功
        SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
        spuInfoEntity.setId(spuId);
        spuInfoEntity.setPublishStatus(1);
        this.updateById(spuInfoEntity);

        /*全程没有做任何可靠性的保证：
            远程调用：每一个OpenFeign接口中，编写降级回调方法；
            本地调用：一定要针对每一个核心环节做一个try...catch处理

         */
    }


    @GlobalTransactional
    @Override
    public Boolean spuadd(SpuAddDTO spuAddDTO) {
        System.out.println(spuAddDTO);
        // 1、spu的基本信息，spu表
        SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
        BeanUtils.copyProperties(spuAddDTO, spuInfoEntity);
        spuInfoEntity.setCatalogId(spuAddDTO.getCategoryId());
        spuInfoEntity.setCreateTime(new Date());
        spuInfoEntity.setUpdateTime(new Date());
        int insert = spuInfoDao.insert(spuInfoEntity);

        System.out.println("insert:" + insert + ",spuinfoEntity:" + spuInfoEntity);
        // 2、spu的图片信息，spu图片表
        List<String> imgUrlPathList = spuAddDTO.getImgUrlPathList();
        for (String s : imgUrlPathList) {
            SpuImagesEntity spuImagesEntity = new SpuImagesEntity();
            spuImagesEntity.setImgUrl(s);
            spuImagesEntity.setSpuId(spuInfoEntity.getId());
            spuImagesDao.insert(spuImagesEntity);
        }
        // 3、spu 详情信息，spu 详情表
        List<String> imgUrlPathListDesc = spuAddDTO.getImgUrlPathListDesc();
        spuInfoDescDao.batchSave(spuInfoEntity.getId(), imgUrlPathListDesc);
        // 4、积分：成长积分、购物积分 jy-coupon.sms_spu_bounds
        SpuBoundsDTO spuBoundsDTO = new SpuBoundsDTO();
        spuBoundsDTO.setSpuId(spuInfoEntity.getId());
        spuBoundsDTO.setGrowBounds(spuAddDTO.getGrowBounds());
        spuBoundsDTO.setBuyBounds(spuAddDTO.getBuyBounds());
        Result save = couponFeignService.save(spuBoundsDTO);

        // 5、基本属性值 pms_product_attr_value
        //spu_id
        //attr_id
        //attr_name`
        //attr_value

        // 属性值表对应的实体类集合
        ArrayList<ProductAttrValueEntity> productAttrValueEntities = new ArrayList<ProductAttrValueEntity>();
        for (Groupattrslist groupattrslist : spuAddDTO.getGroupAttrsList()) {
            for (Baseattrs baseattr : groupattrslist.getBaseattrs()) {
                ProductAttrValueEntity productAttrValueEntity = new ProductAttrValueEntity();
                BeanUtils.copyProperties(baseattr, productAttrValueEntity);
                productAttrValueEntity.setSpuId(spuInfoEntity.getId());
                productAttrValueEntities.add(productAttrValueEntity);
            }
        }
        productAttrValueDao.batchSave(productAttrValueEntities);
        // 6、保存sku信息
        // 6.1 保存sku的基本信息pms_sku_info
        List<Sku> skuList = spuAddDTO.getSku();
        /*
        遍历所有的sku信息，单独保存每一个sku，能获取到当前sku的保存后的sku_id
        拿着这个主键，去拼接skuImages信息，并保存skuImages信息到表中
         */
        for (Sku sku : skuList) {
            SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
            BeanUtils.copyProperties(sku, skuInfoEntity);
            skuInfoDao.insert(skuInfoEntity);
            Long skuId = skuInfoEntity.getSkuId();
            // 6.2 保存sku图片信息 pms_sku_images
            List<String> skuImages = sku.getSkuImages();
            for (String skuImage : skuImages) {
                SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                skuImagesEntity.setSkuId(skuId);
                skuImagesEntity.setImgUrl(skuImage);

                skuImagesDao.insert(skuImagesEntity);

            }
            // 6.3 保存优惠信息：折扣：coupon.sms_sku_ladder  满减：sms_sku_full_reduction
            // 折扣，满几件，打几折
            int fullCount = sku.getFullCount();
            // 折扣，打几折
            int discount = sku.getDiscount();
            // 满减，满多少钱，减多少钱
            int fullPrice = sku.getFullPrice();
            // 减多少钱
            int reducePrice = sku.getReducePrice();
            DiscountsDTO discountsDTO = new DiscountsDTO();
            discountsDTO.setFullCount(fullCount);
            discountsDTO.setDiscount(new BigDecimal(discount));
            discountsDTO.setFullPrice(new BigDecimal(fullPrice));
            discountsDTO.setReducePrice(new BigDecimal(reducePrice));
            // 远程调用coupon微服务，保存这些优惠信息
            Result result = couponFeignService.saveDiscountsInfo(discountsDTO);
            // 6.4 保存会员价格信息  coupon.sms_member_price
            List<Vipprice> vipPriceList = sku.getVipPrice();
//            skuId memberLevelId memberLevelName memberPrice
            List<MemberPriceDTO> memberPriceDTOS = new ArrayList<>();
            // 遍历每一个会员价格信息，封装为每一个MeberProiceDTO
            for (Vipprice vipprice : vipPriceList) {
                Long vipid = vipprice.getVipid();
                String vipname = vipprice.getVipname();
                BigDecimal price = vipprice.getVipprice();
                MemberPriceDTO memberPriceDTO = new MemberPriceDTO();
                memberPriceDTO.setMemberLevelId(vipid);
                memberPriceDTO.setMemberLevelName(vipname);
                memberPriceDTO.setSkuId(skuId);
                memberPriceDTO.setMemberPrice(price);
                memberPriceDTOS.add(memberPriceDTO);
            }
            // 远程调用一次优惠微服务，保存多个会员价格优惠信息
            Result memberPriceResult = couponFeignService.save(memberPriceDTOS);
            // TODO 判断memberPriceResult 失败，就抛出异常，结束当前逻辑
            // 7、销售属性值pms_sku_sale_attr_value
        /*
            我们的表中，是这么记录销售属性值的：是skuId、attrId、attrName、attrValue
            意思就是，销售属性，是和sku相关的， 如果生成sku的时候，没有选中的属性，不应该存储进来
           思路：
            遍历所有的saleAttrs，它对应的是所有的销售属性信息，每一个销售属性长：
             {
            "attrId": 3,
            "attrName": "机身颜色",
            "valueChecked": [
                "黑色",
                "金色"
            ]
        },
            遍历valueChecked所有选中的属性，判断当前skuName是否包含这个属性值名字，例如：
            黑色_8G_128G .contains(黑色);，如果包含，就保存这条销售属性值，对应的一行表中数据：
                skuId、attrId、attrName、 "黑色"
         */
            List<Saleattrs> saleAttrs = spuAddDTO.getSaleAttrs();
            for (Saleattrs saleAttr : saleAttrs) {
                List<String> valueChecked = saleAttr.getValueChecked();
                for (String valueString : valueChecked) {
                    boolean contains = sku.getSkuName().contains(valueString);
                    if (contains) {
                        // 当前正在遍历的销售属性，属于当前的sku的一个销售属性，是可以保存到表
                        SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();
                        skuSaleAttrValueEntity.setAttrId(saleAttr.getAttrId());
                        skuSaleAttrValueEntity.setAttrName(saleAttr.getAttrName());
                        skuSaleAttrValueEntity.setAttrValue(valueString);
                        skuSaleAttrValueEntity.setSkuId(skuId);
                        //存储到表中
                        skuSaleAttrValueDao.insert(skuSaleAttrValueEntity);
                    }
                }
            }
        }
//        int i = 1/0;
        return true;
    }

}